#include "PolyMesh.hpp"
#include <set>
#include "tiny_vector.hpp"
#include <boost/iterator/counting_iterator.hpp>
#include "MeshAlgorithm.hpp"
#include "DebugHelper.hpp"

const int Polyhedron::sTetFaces[4*3] = { 0,1,2, 3,1,0, 3,2,1, 3,0,2 };
const int Polyhedron::sTetTips[4] = { 3, 2, 0, 1 };
const int Polyhedron::sOctFaces[8*3] = { 0,2,1, 0,1,5, 0,5,4, 0,4,2, 3,1,2, 3,5,1, 3,4,5, 3,2,4 };

namespace
{
   Vec3 Midpoint(Vec3 a, Vec3 b) { return (a+b)/2; }
}

PolyMesh::PolyMesh()
{
}

// returns IsReversed()
bool GetFace(boost::array<int, 3>& aFace)
{
   boost::array<int, 3> orig = aFace;
   std::sort(aFace.begin(), aFace.end());
   int eq = ((orig[0] == aFace[0]) ? 1 : 0)
          + ((orig[1] == aFace[1]) ? 1 : 0)
          + ((orig[2] == aFace[2]) ? 1 : 0);
   bool sameOrder = (eq == 0 || eq == 3);
   return !sameOrder;
}

// Polys have been modified, rebuild creases
void PolyMesh::UpdatePolys()
{
   // don't update face-creases meshes without polys
   if ((*polys).empty())
   {
      UpdateFaces();
      return;
   }
   
   crease_faces.clear();

   typedef std::pair<int, int> Mats;
   typedef std::map<Poly::FaceVerts, Mats> FaceMap;
   
   FaceMap faces;
   for (int i = 0; i < polys.size(); ++i)
   {
      Poly& p = polys[i];
      for (int j = 0; j < p.GetFaceCount(); ++j)
      {
         Poly::FaceVerts verts;
         p.GetFace(j, verts);
         bool reversed = GetFace(verts);
         Mats* m;
         FaceMap::iterator iter = faces.find(verts);
         if (iter == faces.end())
         {
            m = &faces[verts];
            *m = Mats(-1,-1);
         }
         else
         {
            m = &iter->second;
         }
         if (reversed)
         {
            m->second = p.material;
         }
         else
         {
            m->first = p.material;
         }
      }
   }
   for (FaceMap::iterator i = faces.begin(); i != faces.end(); ++i)
   {
      Mats& m = i->second;
      if (m.first != m.second)
      {
         Face f;
         f.vertex = i->first;
         f.materials[1] = m.first;
         f.materials[0] = m.second;
         crease_faces.push_back(f);
      }
   }
   
}
void PolyMesh::UpdateFaces()
{
   DEBUG_LOG("PolyMesh::UpdateFaces")
   crease_edges.clear();
   for (size_t i = 0; i < crease_faces.size(); ++i)
   {
      Face& f = crease_faces[i];
      if (f.materials[0] != -1 && f.materials[1] != -1)
      {
         for (int j = 0; j < 3; ++j)
         {
            Edge e = Edge(f.vertex[j], f.vertex[(j+1)%3]);
            OrderEdge(e);
            crease_edges.push_back(e);
         }
      }
   }
   std::sort(crease_edges.begin(), crease_edges.end());
   std::vector<Edge> tmpCreases;
   {
      size_t i(1), j(0);
      for (; i < crease_edges.size();)
      {
         while (i < crease_edges.size() && crease_edges[j] == crease_edges[i])
         {
            ++i;
         }
         int count = i-j;
         if (count != 2)
         {
            tmpCreases.push_back(crease_edges[j]);
         }
         j=i;
      }
   }

   std::swap(*crease_edges, tmpCreases);
   crease_faces.GetEvents().Updated(); 
   crease_edges.GetEvents().Updated();
   DEBUG_LOG(".\n")
}

struct RegionInfo
{
   RegionInterface mRegion;
   EdgeList        mEdges;
};

void PolyMesh::ComputeCreases()
{
   UpdatePolys();
}

void PolyMesh::Updated()
{ 
   verts.GetEvents().Updated(); 
   polys.GetEvents().Updated();
}

void PolyMesh::Recompute()
{
   Updated();
   ComputeCreases();
}
